package com.gooseeker.modbus;

import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.gooseeker.business.MonitorService;
import com.gooseeker.modbus.bean.SensorResultBean;
import com.gooseeker.modbus.bean.StationInfoBean;
import com.gooseeker.util.Constants;

public class SerialPortRecvImpl implements SerialPortEventListener
{
	private static Logger logger = Logger.getLogger(SerialPortRecvImpl.class);

	/**
	 * 最小帧数据长度
	 */
	private static final int MIN_FRAME_SIZE = 21;

	/**
	 * 接收数据缓冲区大小
	 */
	private static final int DEFAULT_RECV_BUFFER_SIZE = 1024;
	/**
	 * 接收数据流句柄
	 */
	private InputStream inputStream = null;
	/**
	 * 接收缓冲区
	 */
	private byte[] readBuffer = null;

	//private static BlockingQueue<Byte> queue = null;
	
	private static List<Byte> list = null;
	
	public static boolean runFlag = false;

	private MonitorService monitorService = null;

	private ParserThread parserThread = null;

	public SerialPortRecvImpl(InputStream inputStream, MonitorService service)
	{
		this.monitorService = service;
		this.readBuffer = new byte[DEFAULT_RECV_BUFFER_SIZE];
		this.inputStream = inputStream;
		//queue = new LinkedBlockingQueue<Byte>(10000);
		list = Collections.synchronizedList(new LinkedList<Byte>());
	}

	@Override
	public void serialEvent(SerialPortEvent event)
	{
		sleep(Constants.CMD_RESEND_PERIOD);
		
		int numBytes = 0;

		switch (event.getEventType())
		{
		case SerialPortEvent.BI: // 10
		case SerialPortEvent.OE: // 7
		case SerialPortEvent.FE: // 9
		case SerialPortEvent.PE: // 8
		case SerialPortEvent.CD: // 6
		case SerialPortEvent.CTS: // 3
		case SerialPortEvent.DSR: // 4
		case SerialPortEvent.RI: // 5
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2
			break;
		case SerialPortEvent.DATA_AVAILABLE: // 1
			try
			{
				// 有数据可读
				// TODO 数据解析要单独出来，以流模式接收数据，根据地址和命令字及校验字段，解析出整帧数据
				// 如果发现当前帧数据不合法则跳过，后续这块要放到线程中单独解析
				// 当前的方案是收到数据就通过延时方法认为接收到的就是一帧完整的数据报文
				// 这样是存在漏洞的，而且解析是线程来完成，也就是说收到一帧起一个线程
				// TODO 应改为数据解析就一个线程，这具Listener只负责接收数据，把数据放到缓冲区内，而
				// 解析线程则不断从中取完整帧进行解析
				while (inputStream.available() > 0)
				{
					// 9600bps,21B=17.5ms,现放大到40ms
					//sleep(120);
					numBytes += inputStream.read(readBuffer);
				}

				for (int i = 0; i < numBytes; i++)
				{
					//queue.offer(readBuffer[i]);
					list.add(readBuffer[i]);
				}

				logger.info("recv: "
						+ ByteUitls.printByteArray(readBuffer, numBytes));
				logger.info("----recv length: " + numBytes);

				// TODO数据入库
				if (null != monitorService)
				{
					if (parserThread == null)
					{
						parserThread = new ParserThread();
						// 如果解析线程未启动，则启动它，如果已启动，则不做任何操作
						if (false == runFlag)
						{
							logger.info("----start parser thread----");
							runFlag = true;
							parserThread.start();
						}
					}
				}
				else
				{
					logger.error("monitorService is null");
					System.out.println("monitorService is null");
				}
			}
			catch (IOException e)
			{
				logger.error("数据读取异常");
				e.printStackTrace();
			}
			break;

		default:
			break;
		}
	}

	class ParserThread extends Thread
	{
		private Map<StationInfoBean, List<SensorResultBean>> dataMap;

		public ParserThread()
		{
			dataMap = new HashMap<StationInfoBean, List<SensorResultBean>>();
		}

		private void parseFrame(byte[] frame)
		{
			logger.info("--IN PARSER THREAD--");

			// 列表中共8个数据bean，相邻两个为一个工位信息
			logger.info("parseData(): "
					+ ByteUitls.printByteArray(frame, frame.length));
			// 将一帧数据解析为list,返回结果存放4个传感器的红线与绿线采集值
			List<SensorResultBean> list = DataAnalysis.parseDataram(frame);
			// int state = 0;
			//StationInfoBean infoBean = new StationInfoBean();

			for (int i = 0; i < list.size(); i++)
			{
				SensorResultBean bean = list.get(i);

				// 判断两个数据的状态
				// state = parseState(bean);

				// 用于缓存当前数据
				SensorResultBean resultBean = new SensorResultBean(bean);
				StationInfoBean infoBean = new StationInfoBean();
				infoBean.setAddress(bean.getDeviceAddr());
				infoBean.setSubAddress(bean.getSensorAddr());

				List<SensorResultBean> tempBeanList = null;
				
				for (StationInfoBean sib : dataMap.keySet())
				{
					if (sib.equals(infoBean))
					{
						tempBeanList = dataMap.get(sib);
						logger.info("----find update key----: addr = " + sib.getAddress() +
								", subaddr = " + sib.getSubAddress());
					}
				}
				//1 如果小于次数则增加到list中
				if (tempBeanList != null && 
					tempBeanList.size() < Constants.CMD_RESEND_TIMES)
				{
					// 如果已存在数据，则向list中再插入数据，插入个数由配置文件决定
					tempBeanList.add(resultBean);
				}
				else
				{
					//2  否则创建一个list
					tempBeanList = new ArrayList<SensorResultBean>();
					tempBeanList.add(resultBean);
					dataMap.put(infoBean, tempBeanList);
				}
				//3 保存数据
				// 存在一种场景，如果采集设备已坏，此时如何侦测？？
				saveData();
			}
		}

		public void run()
		{
			byte[] frame = new byte[MIN_FRAME_SIZE];
			byte[] crc = new byte[2];
			byte[] tempCrc = new byte[2];

			while (runFlag)
			{
				// 从队列中取一个字节，如果队列为空，则一直等待
				try
				{
					if (list.size() < MIN_FRAME_SIZE)
					{
						sleep(100);
						continue;
					}
					
					//确保数据帧的正确性及完整性，需要先取16字节，然后再进行CRC16校验
					//如果是个完整帧，则进行解析，否则继续滑动，直到找到完整帧为止
					tempCrc[0] = 0;
					tempCrc[1] = 0;
					for (int i = 0; i < MIN_FRAME_SIZE - 2; i++)
					{
						frame[i] = list.get(i);
					}
					
					crc[0] = list.get(MIN_FRAME_SIZE - 2);
					crc[1] = list.get(MIN_FRAME_SIZE - 1);
					tempCrc = CRC16Utils.getCrc16(frame, MIN_FRAME_SIZE - 2);
					if (tempCrc[0] != crc[0] || tempCrc[1] != crc[1])
					{
						logger.error("数据帧CRC校验错误，丢弃");
						//丢掉一个数据
						list.remove(0);
						//queue.take();
						continue;
					}
					else
					{
						frame[MIN_FRAME_SIZE - 2] = crc[0];
						frame[MIN_FRAME_SIZE - 1] = crc[1];
						
						boolean flag = checkFrame(frame);
						if (flag == true)
						{
							parseFrame(frame);
						}
						//清空已解析的数据
						for (int i = 0; i < MIN_FRAME_SIZE; i++)
						{
							list.remove(0);
							frame[i] = 0;
						}
					}
				}
				catch (InterruptedException e)
				{
					logger.error("parse thread exception: " + e.getMessage());
				}
			}
			
			frame = null;
			crc = null;
			tempCrc = null;
		}

		private boolean checkFrame(byte[] frame)
		{
			boolean ret = false;
			byte[] crc = new byte[2];
			
			// 如果当前数据不够一帧数据长度，则继续等待
			if (frame.length < MIN_FRAME_SIZE)
			{
				return ret;
			}
			
			byte address = frame[0];
			int addrInt = ByteUitls.byte2Int(address);
			// 如果取出来的地址范围不正确，则丢弃掉不合法帧
			if (addrInt > ConstValue.SLAVE_MAX_ADDR
					|| addrInt < ConstValue.SLAVE_MIN_ADDR)
			{
				logger.error("地址不正确，地址范围应为1~247，实际为" + address);
				return ret;
			}
			
			// 再取出一个字节，判断是否为04，即取数据
			byte cmd = frame[1];
			if (cmd != ConstValue.PROTOCAL_KEYWORD_READ_INPUT)
			{
				// 如果不是04
				logger.error("响应命令不是读取输入电压，实际为：" + cmd);
				return ret;
			}

			byte len = frame[2];
			
			// 最后对整帧数据进行校验
			crc[0] = frame[len + 3];
			crc[1] = frame[len + 4];
			byte[] temp = CRC16Utils.getCrc16(frame, len + 3);
			if (temp[0] != crc[0] || temp[1] != crc[1])
			{
				logger.error("数据帧CRC校验错误，丢弃");
				return ret;
			}
			
			ret = true;
			
			return ret;
		}
		
		private void insertToDB(StationInfoBean info, SensorResultBean result)
		{
			try
			{
				long ret = monitorService.insertMonitorWithAddress(
						info.getAddress(), info.getSubAddress(),
						result.getState(), result.getValue1() + "|" + result.getValue2());

				if (ret <= 0)
				{
					logger.info("数据入库失败：" + ret + "[addr=" + info.getAddress()
							+ ", subaddr=" + info.getSubAddress() + ", value="
							+ result.getValue1() + "|" + result.getValue2() + " state = " + result.getState());
				}
				else
				{
					logger.info("数据入库成功：" + ret + "[addr=" + info.getAddress()
							+ ", subaddr=" + info.getSubAddress() + ", value="
							+ result.getValue1() + "|" + result.getValue2() + " state = " + result.getState());
				}
			}
			catch (Exception e)
			{
				logger.error("insert data to db error: " + e.getMessage());
			}
		}

		//判断数据变动的范围，同一个设备连续多次读取数据，目的是为了排除
		//采集时刻电压可能处在正弦波的特殊位置，比如0，此时的状态会与未使用
		//相同，导致判断状态错误，因此通过多次判断，通过delta方法来判断消除这个
		//误差，如果detla较大，则认为是报警状态，因为只有报警时灯和蜂鸣器才会工作，
		//工作就会导致电压极不稳定，而正常绿灯亮时，电压值比较稳定
		private SensorResultBean checkState(List <SensorResultBean> list)
		{
			SensorResultBean ret = null;
			
			//采集的结果集不为空，且当前个数与配置文件中要求的个数相同
			if (list != null && !list.isEmpty() && 
					list.size() == Constants.CMD_RESEND_TIMES)
			{
				/*
				int state = 0;
				int delta = 0;
				int temp = 0;
				//先取第1个结果
				state = parseState(list.get(0));
				*/
				short line1 = 0;
				short line2 = 0;
				for(int i = 0; i < list.size(); i++)
				{
					line1 += list.get(i).getValue1();
					line2 += list.get(i).getValue2();
				}
				
				line1 = (short)(line1 / list.size());
				line2 = (short)(line2 / list.size());
				
				ret = new SensorResultBean(list.get(0));
				ret.setValue1(line1);
				ret.setValue2(line2);
				
				if (line1 < Constants.UNUSE_RESULT_DELTA && line2 < Constants.UNUSE_RESULT_DELTA)
				{
					ret.setState(ConstValue.SENSOR_STATUS_UNUSE);
				}
				else if (line1 >= Constants.PASS_ADC_THRESHOLD_VALUE && line2 <= Constants.UNUSE_RESULT_DELTA)
				{
					ret.setState(ConstValue.SENSOR_STATUS_PASS);
				}
				else
				{
					ret.setState(ConstValue.SENSOR_STATUS_FAIL);
				}
				
				/*
				switch (state)
				{
				case ConstValue.SENSOR_STATUS_PASS:
					temp = list.get(0).getValue1();
					for (int i = 1; i < list.size(); i++)
					{
						SensorResultBean bean = list.get(i);
						delta += Math.abs(bean.getValue1() - temp);
						temp = bean.getValue1();
					}
					// 如果多次测试结果差的delta
					if (delta <= Constants.PASS_RESULT_DELTA)
					{
						ret = new SensorResultBean(list.get(0));
						ret.setState(ConstValue.SENSOR_STATUS_PASS);
					}
					
					break;
					
				default:
					{
						SensorResultBean tempBean = list.get(0);
						//先判断未使用状态
						temp = tempBean.getValue1() + tempBean.getValue2();
						for (int i = 1; i < list.size(); i++)
						{
							SensorResultBean bean = list.get(i);
							delta += Math.abs(bean.getValue1() + bean.getValue2() - temp);
							temp = bean.getValue1() + bean.getValue2();
							
							if (tempBean.getValue2() < list.get(i).getValue2())
							{
								tempBean = list.get(i);
							}
						}
						
						//如果多次测试的结果差和小于未使用时的阈值，则证明是未使用状态，否则就是报警状态
						if (delta <= Constants.UNUSE_RESULT_DELTA)
						{
							ret = new SensorResultBean(list.get(0));
							
							if (list.get(0).getValue2() > Constants.UNUSE_RESULT_DELTA)
							{
								ret.setState(ConstValue.SENSOR_STATUS_FAIL);
							}
							else
							{
								ret.setState(ConstValue.SENSOR_STATUS_UNUSE);
							}
						}
						else
						{
							//找出采集的值电大的一次
							ret = new SensorResultBean(tempBean);
							ret.setState(ConstValue.SENSOR_STATUS_FAIL);
						}
					}
					break;
				}
				*/
			}
			
			return ret;
		}
		
		private synchronized void saveData()
		{
			List<StationInfoBean> delList = new ArrayList<StationInfoBean>();

			for (StationInfoBean infoBean : dataMap.keySet())
			{
				List <SensorResultBean> resultBeanList = dataMap.get(infoBean);
				SensorResultBean ret = checkState(resultBeanList);
				if (ret != null)
				{
					logger.info("[write data]: addr=" + infoBean.getAddress() + 
							", subaddr="+infoBean.getSubAddress() + " state=" + ret.getState());
					insertToDB(infoBean, ret);
					delList.add(infoBean);
				}
			}

			for (StationInfoBean infoBean : delList)
			{
				dataMap.remove(infoBean);
			}
			delList.clear();
			delList = null;
		}
	}

	/**
	 * 判断两个数据的状态
	 * 
	 * @param bean1
	 * @param bean2
	 * @return
	 */
	@SuppressWarnings("unused")
	private int parseState(SensorResultBean bean)
	{
		int state = ConstValue.SENSOR_STATUS_UNKNOWN;

		// 未使用时，读取到的数据一定是0， 不会存在电感和浪涌冲击
		if (bean.getValue1() == 0 && bean.getValue2() == 0)
		{
			// 该传感器未使用
			state = ConstValue.SENSOR_STATUS_UNUSE;

			return state;
		}

		// 报警时，绿线电压理论上为0，但在现场景复杂生产环境下，可能由于远距离
		// 等环境限制，导致绿线电压有会一定程度的跳变，因此这里考虑一定的阈值范围
		if ((bean.getValue1() >= Constants.PASS_ADC_THRESHOLD_VALUE && bean
				.getValue2() < Constants.FAIL_ADC_THRESHOLD_VALUE))
		{
			// 该传感器正常
			state = ConstValue.SENSOR_STATUS_PASS;

			return state;
		}

		// 告警时电压值应为0.5V以上
		if (bean.getValue1() < Constants.FAIL_ADC_THRESHOLD_VALUE
				&& bean.getValue2() > Constants.FAIL_ADC_THRESHOLD_VALUE)
		{
			state = ConstValue.SENSOR_STATUS_FAIL;

			return state;
		}

		return state;
	}

	class DemoThread extends Thread
	{
		private byte[] test = {0x23, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x04};
		private int index = 0;
		public void run()
		{
			while (true)
			{
				try
				{
					sleep(200);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				
				for (int i = 0; i < 16; i++)
				{
					//queue.offer(test[index % 21]);
					list.add(test[index % 21]);
					//System.out.println(test[index % 21]);
					index++;
				}
			}
		}
	}
	
	public void testParseThread()
	{
		ParserThread pt = new ParserThread();
		byte[] cmds =
		{ 0x23, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1C,
		  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		  0x00, 0x35, 0x04, 0x23, 0x04, 0x10, 0x00, 0x00, 0x00,
		  0x00, 0x02, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		  0x00};

		for (int i = 0; i < cmds.length; i++)
		{
			//queue.offer(cmds[i]);
			list.add(cmds[i]);
		}

		runFlag = true;		
		pt.start();
		
		DemoThread dt = new DemoThread();
		dt.start();
	}

	private void sleep(int ms)
	{
		try
		{
			Thread.sleep(ms);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

	public static void main(String[] args)
	{
		SerialPortRecvImpl ss = new SerialPortRecvImpl(null, null);
		ss.testParseThread();
	}
}
